/*****************************************************************************************
   
   Copyright (c) Microsoft Corporation. All rights reserved.

   Use of this code sample is subject to the terms of the Microsoft
   Permissive License, a copy of which should always be distributed with
   this file.  You can also access a copy of this license agreement at:
   http://www.microsoft.com/resources/sharedsource/licensingbasics/permissivelicense.mspx

 ****************************************************************************************/

using System;

namespace GrooveWebServicesV12Helpers.Discussion
{
	/// <summary>
	/// Represents a (V3) Discussion tool in a Groove workspace.
	/// </summary>
	public class DiscussionTool : 
		GrooveWebServicesV12Helpers.Tool,
		GrooveWebServicesV12Helpers.Shared.IGrooveWebServicesEventCallback
	{
		internal static System.Collections.SortedList s_DiscussionToolEventManagerSortedList = new System.Collections.SortedList();
		private int m_DiscussionToolEventManagerCount = 0;

		private string m_DiscussionSubscriptionID;

		private GrooveDiscussionWebService.GrooveDiscussion m_GrooveDiscussion;

		public DiscussionTool(GrooveWebServicesV12Helpers.Space i_Space, GrooveToolsWebService.Tool i_Tool) :
			base(i_Space, i_Tool)
		{
			//  Make sure we are dealing with a 2.5 or later Discussion tool
			if (this.Type != GrooveWebServicesV12Helpers.Shared.GROOVE_DISCUSSION_TOOL_SUPPORTED_TYPE)
				throw new System.ArgumentException("Tool is not a Groove 2.5 or later Discussion tool!");
		}

		#region Discussion Properties And Methods

		public static System.Data.DataSet CreateDataSetFromDiscussionEntries(GrooveWebServicesV12Helpers.GrooveDiscussionWebService.DiscussionEntry[] i_DiscussionEntries)
		{
			System.Data.DataSet DataSet = new System.Data.DataSet("Data");
			System.Data.DataTable DataTable = int_CreateDataTable();

			foreach (GrooveWebServicesV12Helpers.GrooveDiscussionWebService.DiscussionEntry DiscussionEntry in i_DiscussionEntries)
			{
				System.Data.DataRow DataRow = DataTable.NewRow();
							
				DataRow["ID"] = DiscussionEntry.ID;
				DataRow["URI"] = DiscussionEntry.URI;
				DataRow["CreatedBy"] = DiscussionEntry.CreatedBy;
				DataRow["ModifiedBy"] = DiscussionEntry.ModifiedBy;
				DataRow["Created"] = DiscussionEntry.Created;
				DataRow["Modified"] = DiscussionEntry.Modified;
				DataRow["Unread"] = DiscussionEntry.Unread;
				DataRow["ParentID"] = DiscussionEntry.ParentID;
				DataRow["Subject"] = DiscussionEntry.Subject;
				DataRow["Body"] = DiscussionEntry.Body;

				DataTable.Rows.Add(DataRow);
			}
			
			DataSet.Tables.Add(DataTable);
			DataSet.AcceptChanges();

			return DataSet;
		}

		public static System.Data.DataSet CreateDataSetFromDiscussionEntries(GrooveWebServicesV12Helpers.Discussion.DiscussionEntry[] i_DiscussionEntries)
		{
			System.Data.DataSet DataSet = new System.Data.DataSet("Data");
			System.Data.DataTable DataTable = int_CreateDataTable();

			foreach (GrooveWebServicesV12Helpers.Discussion.DiscussionEntry DiscussionEntry in i_DiscussionEntries)
			{
				System.Data.DataRow DataRow = DataTable.NewRow();
							
				DataRow["ID"] = DiscussionEntry.ID;
				DataRow["URI"] = DiscussionEntry.URI;
				DataRow["CreatedBy"] = DiscussionEntry.CreatedBy;
				DataRow["ModifiedBy"] = DiscussionEntry.ModifiedBy;
				DataRow["Created"] = DiscussionEntry.Created;
				DataRow["Modified"] = DiscussionEntry.Modified;
				DataRow["Unread"] = DiscussionEntry.Unread;
				DataRow["ParentID"] = DiscussionEntry.ParentID;
				DataRow["Subject"] = DiscussionEntry.Subject;
				DataRow["Body"] = DiscussionEntry.Body;

				DataTable.Rows.Add(DataRow);
			}
			
			DataSet.Tables.Add(DataTable);
			DataSet.AcceptChanges();

			return DataSet;
		}

		private static System.Data.DataTable int_CreateDataTable()
		{
			System.Data.DataTable DataTable = new System.Data.DataTable("DiscussionData");
			
			DataTable.Columns.Add("ID", typeof(string));
			DataTable.Columns.Add("URI", typeof(string));
			DataTable.Columns.Add("CreatedBy", typeof(string));
			DataTable.Columns.Add("ModifiedBy", typeof(string));
			DataTable.Columns.Add("Created", typeof(System.DateTime));
			DataTable.Columns.Add("Modified", typeof(System.DateTime));
			DataTable.Columns.Add("Unread", typeof(bool));
			DataTable.Columns.Add("ParentID", typeof(string));
			DataTable.Columns.Add("Subject", typeof(string));
			DataTable.Columns.Add("Body", typeof(string));

			return DataTable;
		}

		public static GrooveDiscussionWebService.GrooveDiscussion CreateInitializedDiscussionWebService(string i_IdentityURL, string i_TargetURI, GrooveWebServicesV12Helpers.Context i_Context)
		{
			//  Create the service
			GrooveDiscussionWebService.GrooveDiscussion DiscussionWebService = new GrooveDiscussionWebService.GrooveDiscussion();

			//  Create the Groove SOAP header
			DiscussionWebService.GrooveRequestHeaderValue = new GrooveDiscussionWebService.GrooveRequestHeader();

			//  Set appropriate header values
			DiscussionWebService.GrooveRequestHeaderValue.GrooveRequestKey = i_Context.RequestKey;
			DiscussionWebService.GrooveRequestHeaderValue.GrooveIdentityURL = i_IdentityURL;

			//  Set the URL
			DiscussionWebService.Url = i_Context.Host + i_TargetURI;

			//  Set Timeout
			DiscussionWebService.Timeout = i_Context.Timeout;

			return DiscussionWebService;
		}

		/// <summary>
		/// Get DiscussionEntry array.
		/// </summary>
		public GrooveWebServicesV12Helpers.Discussion.DiscussionEntry[] DiscussionEntries
		{
			get
			{
				//  Get initialized reference to web service
				GrooveDiscussionWebService.GrooveDiscussion DiscussionWebService = this.GetInitializedDiscussionWebService(this.Data);

				System.DateTime BeginDateTime = System.DateTime.Now;

				//  Ask service to provide the list of discussion entries for this tool
				GrooveDiscussionWebService.DiscussionEntry[] DiscussionEntries = DiscussionWebService.Read();

				if (GrooveWebServicesV12Helpers.Context.TraceEnabled)
				{
					System.TimeSpan TimeSpan = System.DateTime.Now.Subtract(BeginDateTime);
					System.Diagnostics.Debug.WriteLine("DiscussionWebService::Read, " + TimeSpan.TotalMilliseconds + " ms");
				}

				//  Create array list of discussion entries
				System.Collections.ArrayList DiscussionEntryArrayList = new System.Collections.ArrayList();
				foreach (GrooveDiscussionWebService.DiscussionEntry DiscussionEntry in DiscussionEntries)
				{
					GrooveWebServicesV12Helpers.Discussion.DiscussionEntry NewDiscussionEntry = new GrooveWebServicesV12Helpers.Discussion.DiscussionEntry
						(
						this,
						DiscussionEntry
						);

					DiscussionEntryArrayList.Add(NewDiscussionEntry);
				}

				//  Sort array list (default is by creation date)
				DiscussionEntryArrayList.Sort();

				//  Return array from array list
				return (GrooveWebServicesV12Helpers.Discussion.DiscussionEntry[])DiscussionEntryArrayList.ToArray(typeof(GrooveWebServicesV12Helpers.Discussion.DiscussionEntry));
			}
		}

		/// <summary>
		/// Find discussion entries matching a keyword in their Subject or Body
		/// </summary>
		/// <param name="i_Keyword">Partial text (case insensitive)</param>
		/// <returns>DiscussionEntry array</returns>
		public GrooveWebServicesV12Helpers.Discussion.DiscussionEntry[] FindMatchingEntries(string i_Keyword)
		{
			//  Check for valid keyword
			if (i_Keyword.Length == 0)
				throw new System.ArgumentException("No keyword specified!");

			string NormalizedKeyword = i_Keyword.ToLower();

			//  Get list of discussion entries for this tool
			GrooveWebServicesV12Helpers.Discussion.DiscussionEntry[] DiscussionEntries = this.DiscussionEntries;

			System.Collections.ArrayList DiscussionEntryArrayList = new System.Collections.ArrayList();

			//  Only do work if discussion entries exist
			if (DiscussionEntries.Length > 0)
			{
				//  Iterate through discussion entries
				foreach (GrooveWebServicesV12Helpers.Discussion.DiscussionEntry DiscussionEntry in DiscussionEntries)
				{
					string NormalizedSubject = DiscussionEntry.Subject.ToLower();
					string NormalizedBody = DiscussionEntry.Body.ToLower();

					bool FoundMatch = 
						(NormalizedSubject.IndexOf(NormalizedKeyword) >= 0) ||
						(NormalizedBody.IndexOf(NormalizedKeyword) >= 0);

					if (FoundMatch)
						DiscussionEntryArrayList.Add(DiscussionEntry);
				}
			}

			//  Return array from array list
			return (GrooveWebServicesV12Helpers.Discussion.DiscussionEntry[])DiscussionEntryArrayList.ToArray(typeof(GrooveWebServicesV12Helpers.Discussion.DiscussionEntry));
		}

		/// <summary>
		/// Add a new Discussion entry.
		/// </summary>
		/// <param name="i_Subject">Subject</param>
		/// <param name="i_Body">Body</param>
		/// <returns>URI of the new entry.</returns>
		public string AddEntry(string i_Subject, string i_Body)
		{
			//  Get initialized reference to web service
			GrooveDiscussionWebService.GrooveDiscussion DiscussionWebService = this.GetInitializedDiscussionWebService(this.Data);

			//  Set parameters
			GrooveDiscussionWebService.DiscussionEntry DiscussionEntry = new GrooveDiscussionWebService.DiscussionEntry();
			DiscussionEntry.Subject = i_Subject;
			DiscussionEntry.Body = i_Body;
			DiscussionEntry.ParentID = ""; // Top level entry

			System.DateTime BeginDateTime = System.DateTime.Now;

			//  Ask service to create discussion entry
			string DiscussionEntryURI = DiscussionWebService.Create(DiscussionEntry);

			if (GrooveWebServicesV12Helpers.Context.TraceEnabled)
			{
				System.TimeSpan TimeSpan = System.DateTime.Now.Subtract(BeginDateTime);
				System.Diagnostics.Debug.WriteLine("DiscussionWebService::Create, " + TimeSpan.TotalMilliseconds + " ms");
			}

			return DiscussionEntryURI;
		}

		/// <summary>
		/// Open a DiscussionEntry by URI
		/// </summary>
		/// <param name="i_DiscussionEntryURI">URI of the DiscussionEntry</param>
		/// <returns>DiscussionEntry</returns>
		public GrooveWebServicesV12Helpers.Discussion.DiscussionEntry OpenEntry(string i_DiscussionEntryURI)
		{
			//  Get initialized reference to web service
			GrooveDiscussionWebService.GrooveDiscussion DiscussionWebService = this.GetInitializedDiscussionWebService(i_DiscussionEntryURI);

			System.DateTime BeginDateTime = System.DateTime.Now;

			//  Ask service to read discussion entry
			GrooveDiscussionWebService.DiscussionEntry ExistingDiscussionEntry = DiscussionWebService.ReadEntry();

			if (GrooveWebServicesV12Helpers.Context.TraceEnabled)
			{
				System.TimeSpan TimeSpan = System.DateTime.Now.Subtract(BeginDateTime);
				System.Diagnostics.Debug.WriteLine("DiscussionWebService::ReadEntry, " + TimeSpan.TotalMilliseconds + " ms");
			}

			//  Create helper class
			GrooveWebServicesV12Helpers.Discussion.DiscussionEntry DiscussionEntry = new GrooveWebServicesV12Helpers.Discussion.DiscussionEntry
				(
				this,
				ExistingDiscussionEntry
				);

			return DiscussionEntry;
		}

		/// <summary>
		/// Delete a Discussion entry by URI
		/// </summary>
		/// <param name="i_DiscussionEntryURI">URI of the DiscussionEntry</param>
		public void RemoveEntry(string i_DiscussionEntryURI)
		{
			//  Get initialized reference to web service
			GrooveDiscussionWebService.GrooveDiscussion DiscussionWebService = this.GetInitializedDiscussionWebService(this.Data);

			System.DateTime BeginDateTime = System.DateTime.Now;

			//  Ask service to delete discussion entry
			DiscussionWebService.DeleteEntry();

			if (GrooveWebServicesV12Helpers.Context.TraceEnabled)
			{
				System.TimeSpan TimeSpan = System.DateTime.Now.Subtract(BeginDateTime);
				System.Diagnostics.Debug.WriteLine("DiscussionWebService::DeleteEntry, " + TimeSpan.TotalMilliseconds + " ms");
			}
		}

		private GrooveDiscussionWebService.GrooveDiscussion GetInitializedDiscussionWebService(string i_TargetURI)
		{
            if ((m_GrooveDiscussion == null) || Context.ReinitNeeded(m_GrooveDiscussion.GrooveRequestHeaderValue.GrooveRequestKey))
			{
				m_GrooveDiscussion = GrooveWebServicesV12Helpers.Discussion.DiscussionTool.CreateInitializedDiscussionWebService
					(
					m_IdentityURL,
					i_TargetURI,
					Context
					);
			}
			else
			{
				//  Explicitly overwrite target URL and Timeout
				m_GrooveDiscussion.Url = Context.Host + i_TargetURI;
                m_GrooveDiscussion.Timeout = Context.Timeout;
			}

			return m_GrooveDiscussion;
		}

		#endregion

		#region Discussion Event Handling

		private void IncrementDiscussionToolSubscriptionCount()
		{
			lock (s_DiscussionToolEventManagerSortedList)
			{
				if (m_DiscussionToolEventManagerCount == 0)
				{
					m_DiscussionSubscriptionID = GrooveWebServicesV12Helpers.EventManager.AddEventListener
						(
						Context,
						this.IdentityURL,
						GrooveWebServicesV12Helpers.Shared.GROOVE_WEB_SERVICES_DISCUSSION_EVENT_CLASS, 
						this.Data,
						GrooveWebServicesV12Helpers.EventManager.s_EventURI,
						this
						);
				}

				++m_DiscussionToolEventManagerCount;
			}
		}

		private void DecrementDiscussionToolSubscriptionCount()
		{
			lock (s_DiscussionToolEventManagerSortedList)
			{
				--m_DiscussionToolEventManagerCount;

				if (m_DiscussionToolEventManagerCount == 0)
				{
					GrooveWebServicesV12Helpers.EventManager.RemoveEventListener
						(
						m_DiscussionSubscriptionID,
						this
						);

					m_DiscussionSubscriptionID = null;
				}
			}
		}

		internal void RaiseDiscussionEntriesAddedEvent(GrooveDiscussionWebService.DiscussionEntry[] i_DiscussionEntries)
		{
			//  If one or more subscribers, broadcast event
			if (InternalOnDiscussionEntriesAddedEvent != null)
			{
				//  Create array list of discussion entries
				System.Collections.ArrayList DiscussionEntryArrayList = new System.Collections.ArrayList();

				foreach (GrooveDiscussionWebService.DiscussionEntry DiscussionEntry in i_DiscussionEntries)
				{
					GrooveWebServicesV12Helpers.Discussion.DiscussionEntry NewDiscussionEntry = new GrooveWebServicesV12Helpers.Discussion.DiscussionEntry
						(
						this,
						DiscussionEntry
						);

					DiscussionEntryArrayList.Add(NewDiscussionEntry);
				}

				//  Convert array list to array
				GrooveWebServicesV12Helpers.Discussion.DiscussionEntry[] DiscussionEntries = (GrooveWebServicesV12Helpers.Discussion.DiscussionEntry[])DiscussionEntryArrayList.ToArray(typeof(GrooveWebServicesV12Helpers.Discussion.DiscussionEntry));

				InternalOnDiscussionEntriesAddedEvent(DiscussionEntries);
			}
		}

		internal void RaiseDiscussionEntriesRemovedEvent(string[] i_DiscussionIDs)
		{
			//  If one or more subscribers, broadcast event
			if (InternalOnDiscussionEntriesRemovedEvent != null)
				InternalOnDiscussionEntriesRemovedEvent(i_DiscussionIDs);
		}

		internal void RaiseDiscussionEntriesUpdatedEvent(GrooveDiscussionWebService.DiscussionEntry[] i_DiscussionEntries)
		{
			//  If one or more subscribers, broadcast event
			if (InternalOnDiscussionEntriesUpdatedEvent != null)
			{
				//  Create array list of discussion entries
				System.Collections.ArrayList DiscussionEntryArrayList = new System.Collections.ArrayList();

				foreach (GrooveDiscussionWebService.DiscussionEntry DiscussionEntry in i_DiscussionEntries)
				{
					GrooveWebServicesV12Helpers.Discussion.DiscussionEntry NewDiscussionEntry = new GrooveWebServicesV12Helpers.Discussion.DiscussionEntry
						(
						this,
						DiscussionEntry
						);

					DiscussionEntryArrayList.Add(NewDiscussionEntry);
				}

				//  Convert array list to array
				GrooveWebServicesV12Helpers.Discussion.DiscussionEntry[] DiscussionEntries = (GrooveWebServicesV12Helpers.Discussion.DiscussionEntry[])DiscussionEntryArrayList.ToArray(typeof(GrooveWebServicesV12Helpers.Discussion.DiscussionEntry));

				InternalOnDiscussionEntriesUpdatedEvent(DiscussionEntries);
			}
		}

		public delegate void OnDiscussionEntriesAddedDelegate(GrooveWebServicesV12Helpers.Discussion.DiscussionEntry[] i_DiscussionEntries);
		internal event OnDiscussionEntriesAddedDelegate InternalOnDiscussionEntriesAddedEvent;
		public event OnDiscussionEntriesAddedDelegate OnDiscussionEntriesAddedEvent
		{
			add
			{
				IncrementDiscussionToolSubscriptionCount();
				InternalOnDiscussionEntriesAddedEvent += value;
			}
			remove
			{
				InternalOnDiscussionEntriesAddedEvent -= value;
				DecrementDiscussionToolSubscriptionCount();
			}
		}

		public delegate void OnDiscussionEntriesRemovedDelegate(string[] i_DiscussionIDArray);
		internal event OnDiscussionEntriesRemovedDelegate InternalOnDiscussionEntriesRemovedEvent;
		public event OnDiscussionEntriesRemovedDelegate OnDiscussionEntriesRemovedEvent
		{
			add
			{
				IncrementDiscussionToolSubscriptionCount();
				InternalOnDiscussionEntriesRemovedEvent += value;
			}
			remove
			{
				InternalOnDiscussionEntriesRemovedEvent -= value;
				DecrementDiscussionToolSubscriptionCount();
			}
		}

		public delegate void OnDiscussionEntriesUpdatedDelegate(GrooveWebServicesV12Helpers.Discussion.DiscussionEntry[] i_DiscussionEntries);
		internal event OnDiscussionEntriesUpdatedDelegate InternalOnDiscussionEntriesUpdatedEvent;
		public event OnDiscussionEntriesUpdatedDelegate OnDiscussionEntriesUpdatedEvent
		{
			add
			{
				IncrementDiscussionToolSubscriptionCount();
				InternalOnDiscussionEntriesUpdatedEvent += value;
			}
			remove
			{
				InternalOnDiscussionEntriesUpdatedEvent -= value;
				DecrementDiscussionToolSubscriptionCount();
			}
		}

		#endregion

		#region GrooveWebServicesV12HelpersBase Implementation

		public override object BindToObject(ref string[] i_URLParts, int i_URLPartIndex)
		{
			object BindResult = null;

			string DiscussionEntryURI = this.Data + "/" + i_URLParts[i_URLPartIndex];

			try
			{
				BindResult = this.OpenEntry(DiscussionEntryURI);
			}
			catch (System.Exception Exception)
			{
				throw new System.Exception("Unable to find entry '" + DiscussionEntryURI +"' (" + Exception.Message + ")");
			}

			return BindResult;
		}

		#endregion
	
		#region IGrooveWebServicesEventCallback Members

		public void ProcessEvent(GrooveEventsWebService.Event i_Event)
		{
			switch (i_Event.EventType)
			{
				case GrooveWebServicesV12Helpers.Shared.GROOVE_DISCUSSION_ADD_EVENT_TYPE:
				{
					GrooveEventsWebService.GrooveDiscussionAddEventData DiscussionAddEventData = (GrooveEventsWebService.GrooveDiscussionAddEventData)i_Event.EventData;
					this.RaiseDiscussionEntriesAddedEvent(DiscussionAddEventData.DiscussionEntries);

					break;
				}

				case GrooveWebServicesV12Helpers.Shared.GROOVE_DISCUSSION_DELETE_EVENT_TYPE:
				{
					GrooveEventsWebService.GrooveDiscussionDeleteEventData DiscussionDeleteEventData = (GrooveEventsWebService.GrooveDiscussionDeleteEventData)i_Event.EventData;
					this.RaiseDiscussionEntriesRemovedEvent(DiscussionDeleteEventData.DiscussionIDs);

					break;
				}

				case GrooveWebServicesV12Helpers.Shared.GROOVE_DISCUSSION_UPDATE_EVENT_TYPE:
				{
					GrooveEventsWebService.GrooveDiscussionUpdateEventData DiscussionUpdateEventData = (GrooveEventsWebService.GrooveDiscussionUpdateEventData)i_Event.EventData;
					this.RaiseDiscussionEntriesUpdatedEvent(DiscussionUpdateEventData.DiscussionEntries);

					break;
				}

				default:
				{
					System.Diagnostics.Debug.WriteLine("[DiscussionTool]Unknown event type: " + i_Event.EventType);
					break;
				}
			}
		}

		#endregion
	}
}
